/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/slab.h>
#include <linux/list.h>

#include "mrp.h"
#include "mrp_map.h"
#include "mrp_private.h"

static int map_mrp_mad_join_indication(struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type, int new)
{
    struct mrp_map * map;
    struct mrp * m;
    
    MRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    map = mrp_get_mrp_map(mrp);
    
    spin_lock(&map->mrps_lock);
    list_for_each_entry(m, &map->mrps, map_list)
    {
        mrp_mad_join_request_notify(m, mrp_attribute_type, attribute_value, four_packed_type, new);
    }
    spin_unlock(&map->mrps_lock);
    
    return 0;
}

static int map_mrp_mad_leave_indication(struct mrp * mrp, int mrp_attribute_type, void * attribute_value, int four_packed_type)
{
    struct mrp_map * map;
    struct mrp * m;
    
    MRP_PRINT_INFO ("%s\n", __FUNCTION__);
    
    map = mrp_get_mrp_map(mrp);
    
    spin_lock(&map->mrps_lock);
    list_for_each_entry(m, &map->mrps, map_list)
    {
        mrp_mad_leave_request_notify(m, mrp_attribute_type, attribute_value);
    }
    spin_unlock(&map->mrps_lock);
    
    return 0;
}

int mrp_map_init (struct mrp_map * mrp_map)
{
	memset (mrp_map, 0, sizeof (struct mrp_map));
    
    spin_lock_init(&mrp_map->mrps_lock);
    INIT_LIST_HEAD(&mrp_map->mrps);

	return 0;
}

void mrp_map_cleanup (struct mrp_map * mrp_map)
{
	/* nothing for now */
}

struct mrp_map * mrp_map_create_and_init(void)
{
    struct mrp_map * map;
    
    if (!(map = kzalloc(sizeof(struct mrp_map), GFP_KERNEL)))
        return NULL;
        
    if (mrp_map_init(map))
    {
        mrp_map_destroy(&map);
        return NULL;
    }
    
    return map;
}

void mrp_map_destroy(struct mrp_map ** mrp_map)
{
    struct mrp_map * map;
    
    map = *mrp_map;
    
    if (map)
    {
        mrp_map_cleanup(map);
        kfree(map);
        *mrp_map = NULL;
    }
}

void mrp_map_set_mrp(struct mrp_map * mrp_map, struct mrp * mrp)
{
    mrp_map->mrp = mrp;
    mrp_add_indication_functions(mrp, map_mrp_mad_join_indication, map_mrp_mad_leave_indication);
}

int mrp_map_add_mrp(struct mrp_map * mrp_map, struct mrp * mrp)
{
    spin_lock(&mrp_map->mrps_lock);    
    list_add_tail(&mrp->map_list, &mrp_map->mrps);
    spin_unlock(&mrp_map->mrps_lock);
    return 0;
}

int mrp_map_remove_mrp(struct mrp_map * mrp_map, struct mrp * mrp)
{
    struct mrp * m;
    struct mrp * m_next;
    
    spin_lock(&mrp_map->mrps_lock);
    list_for_each_entry_safe (m, m_next, &mrp_map->mrps, map_list)
    {
        if (m == mrp)
            list_del_init(&m->map_list);
    }
    spin_unlock(&mrp_map->mrps_lock);
    return 0;
}
